{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tce3stUlHN0L"
      },
      "source": [
        "##### Copyright 2025 Google LLC."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tuOe1ymfHZPu"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BtmHJorbyUpN"
      },
      "source": [
        "# ADK Simple Demo: Stateful Echo Agent with Gemini\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ubGE8QlE5Sck"
      },
      "source": [
        "<a target=\"_blank\" href=\"https://colab.research.google.com/github/google-gemini/cookbook/blob/main/examples/google-adk/Getting_started_with_ADK.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" height=30/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dIKUwcq75dvc"
      },
      "source": [
        "This notebook provides a basic, introductory example of using `Gemini` in the Google Agent Development Kit (ADK).\n",
        "\n",
        "**Goal:** Demonstrate how ADK orchestrates a simple workflow involving state transitions (`START` -> `PROCESSING` -> `END`) around a core interaction with the Gemini API.\n",
        "\n",
        "**Scenario:**\n",
        "You will build a \"Stateful Echo Agent\". This agent's primary task is to echo the user's input. However, it will use ADK components to manage its internal state throughout the process:\n",
        "1.  It starts in a `START` state.\n",
        "2.  Upon receiving input, it uses an ADK Tool to transition to `PROCESSING`.\n",
        "3.  It prepares the echo response (implicitly using the Gemini model configured in the Agent).\n",
        "4.  It uses the ADK Tool again to transition to the `END` state.\n",
        "5.  It delivers the final echo response.\n",
        "\n",
        "This example highlights ADK's role in managing structured workflows and state, even for simple tasks."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ae6603fc5bdc"
      },
      "source": [
        "<!-- Community Contributor Badge -->\n",
        "<table>\n",
        "  <tr>\n",
        "    <!-- Author Avatar Cell -->\n",
        "    <td bgcolor=\"#d7e6ff\">\n",
        "      <a href=\"https://github.com/andycandy\" target=\"_blank\" title=\"View Anand Roy's profile on GitHub\">\n",
        "        <img src=\"https://github.com/andycandy.png?size=100\"\n",
        "             alt=\"andycandy's GitHub avatar\"\n",
        "             width=\"100\"\n",
        "             height=\"100\">\n",
        "      </a>\n",
        "    </td>\n",
        "    <!-- Text Content Cell -->\n",
        "    <td bgcolor=\"#d7e6ff\">\n",
        "      <h2><font color='black'>This notebook was contributed by <a href=\"https://github.com/andycandy\" target=\"_blank\"><font color='#217bfe'><strong>Anand Roy</strong></font></a>.</font></h2>\n",
        "      <h5><font color='black'><a href=\"https://www.linkedin.com/in/anand-roy-61a2b529b\"><font color=\"#078efb\">LinkedIn</font></a> - See <a href=\"https://github.com/andycandy\" target=\"_blank\"><font color=\"#078efb\"><strong>Anand</strong></font></a> other notebooks <a href=\"https://github.com/search?q=repo%3Agoogle-gemini%2Fcookbook%20%22Anand%20Roy%22&type=code\" target=\"_blank\"><font color=\"#078efb\">here</font></a>.</h5></font><br>\n",
        "      <!-- Footer -->\n",
        "      <font color='black'><small><em>Have a cool Gemini example? Feel free to <a href=\"https://github.com/google-gemini/cookbook/blob/main/CONTRIBUTING.md\" target=\"_blank\"><font color=\"#078efb\">share it too</font></a>!</em></small></font>\n",
        "    </td>\n",
        "  </tr>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "41fbd6a3290a"
      },
      "source": [
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ry9BrzkQ5Jis"
      },
      "outputs": [],
      "source": [
        "%pip install -q google-adk google-genai python-dotenv"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vdMmMi_8yhKp"
      },
      "source": [
        "## 1. Configure Google API Key\n",
        "\n",
        "To power the `Agent` with Gemini, access to the Google Generative AI API is required. The next code cell configures your API key.\n",
        "\n",
        "**Important:** This example uses Colab Secrets (`userdata.get('GOOGLE_API_KEY')`). Make sure you have stored your key named `GOOGLE_API_KEY` in the Colab Secrets manager (View -> Secrets)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yFHNhJ8lvbuG"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from google.colab import userdata\n",
        "import os\n",
        "from dotenv import load_dotenv\n",
        "\n",
        "api_key = userdata.get('GOOGLE_API_KEY')\n",
        "os.environ['GOOGLE_API_KEY'] = api_key\n",
        "load_dotenv()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OlgVxWeXysBT"
      },
      "source": [
        "## 2. Core ADK Components in this Demo\n",
        "\n",
        "This example uses the following key ADK components:\n",
        "\n",
        "*   **`Agent`**: The agent powered by the Gemini model. It understands instructions, decides when to use tools, and generates responses.\n",
        "*   **`FunctionTool`**: A custom capability provided to the agent. In this case, it's a tool to update the workflow status.\n",
        "*   **`ToolContext`**: An object automatically passed to our tool, allowing it to access and modify the `Session State`.\n",
        "*   **`SessionService` (`InMemorySessionService`)**: Manages the conversation's state (`workflow_status`). `InMemory` means the state exists only while this script runs.\n",
        "*   **`Runner`**: Orchestrates the entire interaction: passes user input to the agent, handles tool calls, manages the state via the `SessionService`, and delivers the final response.\n",
        "*   **`Session State`**: A dictionary holding data for the current conversation (session). Here, you use it to store `{'workflow_status': '...'}`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2gBwoyW-Fjd4"
      },
      "outputs": [],
      "source": [
        "from google.adk.agents import Agent\n",
        "from google.adk.tools import FunctionTool, ToolContext\n",
        "from google.adk.sessions import InMemorySessionService\n",
        "from google.adk.runners import Runner\n",
        "from google.adk.sessions import Session"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BYswwY2bFs6E"
      },
      "source": [
        "## 3. Define ADK Components (Tool, Agent, Services)\n",
        "\n",
        "Now, let's define the core ADK components for our Stateful Echo Agent:\n",
        "\n",
        "1.  **Tool (`set_workflow_state`):** A Python function wrapped as an ADK `FunctionTool`. This function will modify the `workflow_status` in the session state when called by the agent.\n",
        "2.  **Agent (`echo_agent`):** An `LlmAgent` configured with the Gemini model, specific instructions on *when* to call the `state_tool`, and the tool itself.\n",
        "3.  **Services (`session_service`, `runner`):** The `InMemorySessionService` to hold state and the `Runner` to execute the agent.\n",
        "4.  **Session:** Used to create a specific session instance with an initial state `{'workflow_status': 'START'}`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j6uyl-EZyw6v"
      },
      "outputs": [],
      "source": [
        "async def set_workflow_state(state_name: str, tool_context: ToolContext) -> dict:\n",
        "    \"\"\"Sets the current workflow state in the session state.\n",
        "\n",
        "    Use this tool to mark progress through the workflow stages:\n",
        "    - Call with 'PROCESSING' before handling the user input.\n",
        "    - Call with 'END' after handling the user input.\n",
        "\n",
        "    Args:\n",
        "        state_name: The state to set (e.g., 'PROCESSING', 'END').\n",
        "        tool_context: Injected context to access session state.\n",
        "\n",
        "    Returns:\n",
        "        A dictionary confirming the status update.\n",
        "    \"\"\"\n",
        "    try:\n",
        "        tool_context.state['workflow_status'] = state_name\n",
        "        return {'status': 'success', 'message': f'Workflow state set to {state_name}'}\n",
        "    except Exception as e:\n",
        "        return {'status': 'error', 'message': f'Failed to set state: {e}'}\n",
        "\n",
        "# Create the function tool\n",
        "state_tool = FunctionTool(func=set_workflow_state)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2EvjrLw8yzkW"
      },
      "outputs": [],
      "source": [
        "GEMINI_MODEL = \"gemini-2.5-flash\" # @param [\"gemini-2.5-flash-lite\", \"gemini-2.5-flash\", \"gemini-2.5-pro\",\"gemini-3-pro-preview\"] {\"allow-input\":true, isTemplate: true}\n",
        "\n",
        "echo_agent = Agent(\n",
        "  name=\"EchoAgent\",\n",
        "  description=\"An agent that echoes input while tracking workflow state.\",\n",
        "  model=GEMINI_MODEL,\n",
        "  instruction=\"\"\"\n",
        "    You are a simple echo agent. You also manage a workflow status stored in the session state under the key 'workflow_status'.\n",
        "    The workflow states are: START, PROCESSING, END.\n",
        "\n",
        "    Your Workflow:\n",
        "    1. The workflow starts in the 'START' state (this is set externally).\n",
        "    2. When you receive user input:\n",
        "        a. FIRST, use the 'set_workflow_state' tool to change the status to 'PROCESSING'.\n",
        "        b. THEN, simply repeat the user's exact input back to them in your response text.\n",
        "        c. AFTER preparing the echo response text, use the 'set_workflow_state' tool AGAIN to change the status to 'END'.\n",
        "        d. FINALLY, provide only the echo response text to the user.\n",
        "  \"\"\",\n",
        "  tools=[state_tool],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qVa87q1uy1Qx"
      },
      "outputs": [],
      "source": [
        "session_service = InMemorySessionService()\n",
        "runner = Runner(\n",
        "    agent=echo_agent,\n",
        "    session_service=session_service,\n",
        "    app_name=\"EchoAgentDemo\"\n",
        ")\n",
        "\n",
        "APP_NAME=\"EchoAgentDemo\"\n",
        "USER_ID=\"1\"\n",
        "ID=\"session_01\"\n",
        "\n",
        "session = session_service.create_session(\n",
        "    app_name=APP_NAME,\n",
        "    user_id=USER_ID,\n",
        "    session_id=ID,\n",
        "    state={'workflow_status': 'START'}\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9CDKuEFJ0U0j"
      },
      "source": [
        "## 4. Run the Interaction\n",
        "\n",
        "Now send a simple message (\"Hello ADK!\") to the `echo_agent` via the `Runner`. The `Runner` will manage the execution flow according to the agent's instructions.\n",
        "\n",
        "**Expected Flow:**\n",
        "1.  Agent receives \"Hello ADK!\".\n",
        "2.  Agent calls `set_workflow_state` tool (state -> `PROCESSING`).\n",
        "3.  Agent calls `set_workflow_state` tool (state -> `END`).\n",
        "4.  Agent responds with the text \"Hello ADK!\".\n",
        "\n",
        "The next cell initiates the `run_async` call and processes the stream of events generated during execution, logging the steps."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kotHsuNt0Uc7"
      },
      "outputs": [],
      "source": [
        "from google.genai.types import Content, Part\n",
        "\n",
        "user_input_text = \"Hello ADK!\"\n",
        "user_message = Content(role='user', parts=[Part(text=user_input_text)])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9Bp2ol4X0Z9n"
      },
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "WARNING:google_genai.types:Warning: there are non-text parts in the response: ['function_call'],returning concatenated text result from text parts,check out the non text parts for full response from model.\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "[Event 1] Type: Event\n",
            "  Role: model\n",
            "  >>> Function Call <<<\n",
            "      Name: set_workflow_state\n",
            "      Args: {'state_name': 'PROCESSING'}\n",
            "\n",
            "[Event 2] Type: Event\n",
            "  Role: user\n",
            "  <<< Function Response >>>\n",
            "      Name: set_workflow_state\n",
            "      Data: {'status': 'success', 'message': 'Workflow state set to PROCESSING'}\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "WARNING:google_genai.types:Warning: there are non-text parts in the response: ['function_call'],returning concatenated text result from text parts,check out the non text parts for full response from model.\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "[Event 3] Type: Event\n",
            "  Role: model\n",
            "  >>> Function Call <<<\n",
            "      Name: set_workflow_state\n",
            "      Args: {'state_name': 'END'}\n",
            "\n",
            "[Event 4] Type: Event\n",
            "  Role: user\n",
            "  <<< Function Response >>>\n",
            "      Name: set_workflow_state\n",
            "      Data: {'status': 'success', 'message': 'Workflow state set to END'}\n",
            "\n",
            "[Event 5] Type: Event\n",
            "  Role: model\n",
            "  Text: 'Hello ADK!\n",
            "'\n"
          ]
        }
      ],
      "source": [
        "final_agent_response_text = None\n",
        "async def process_interaction_events():\n",
        "    \"\"\"Helper async function to process events.\"\"\"\n",
        "    global final_agent_response_text\n",
        "    event_count = 0\n",
        "    async for event in runner.run_async(session_id=ID, new_message=user_message, user_id=USER_ID):\n",
        "        event_count += 1\n",
        "        print(f\"\\n[Event {event_count}] Type: {type(event).__name__}\")\n",
        "\n",
        "        if event.content:\n",
        "            part = event.content.parts[0]\n",
        "            role = event.content.role\n",
        "            print(f\"  Role: {role}\")\n",
        "            if part.text:\n",
        "                print(f\"  Text: '{part.text}'\")\n",
        "                if role == 'model':\n",
        "                    final_agent_response_text = part.text\n",
        "            elif part.function_call:\n",
        "                print(f\"  >>> Function Call <<<\")\n",
        "                print(f\"      Name: {part.function_call.name}\")\n",
        "                print(f\"      Args: {part.function_call.args}\")\n",
        "            elif part.function_response:\n",
        "                print(f\"  <<< Function Response >>>\")\n",
        "                print(f\"      Name: {part.function_response.name}\")\n",
        "                print(f\"      Data: {part.function_response.response}\")\n",
        "\n",
        "await process_interaction_events()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QNDxGtxI0cCR"
      },
      "source": [
        "## 5. Analyze the Results\n",
        "\n",
        "Examine the \"Agent Event Log\" printed above. You should clearly see the sequence reflecting the agent's instructions:\n",
        "\n",
        "1.  `Function Call` event targeting `set_workflow_state` with `args={'state_name': 'PROCESSING'}`.\n",
        "2.  `Function Response` event confirming the first tool execution.\n",
        "3.  `Function Call` event targeting `set_workflow_state` with `args={'state_name': 'END'}`.\n",
        "4.  `Function Response` event confirming the second tool execution.\n",
        "5.  final event containing the agent's text response (the echoed message).\n",
        "\n",
        "The next cell verifies this outcome by checking the final state stored in the session."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LJaeajJu0ce0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Final workflow status: END\n",
            "Agent response: Hello ADK!\n",
            "\n"
          ]
        }
      ],
      "source": [
        "final_session = session_service.get_session(\n",
        "    session_id=session.id,\n",
        "    user_id=USER_ID,\n",
        "    app_name=APP_NAME\n",
        ")\n",
        "\n",
        "if final_session:\n",
        "    final_state = final_session.state\n",
        "    workflow_status = final_state.get('workflow_status')\n",
        "    print(f\"Final workflow status: {workflow_status}\")\n",
        "\n",
        "if final_agent_response_text:\n",
        "    print(f\"Agent response: {final_agent_response_text}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "44M5Rl7oJ7q5"
      },
      "source": [
        "## Next Steps & Further Learning\n",
        "\n",
        "This notebook demonstrated the basic structure of an ADK application, including:\n",
        "\n",
        "*   Defining an `Agent` powered by Gemini.\n",
        "*   Creating a simple `FunctionTool` to modify state.\n",
        "*   Using `SessionService` and `ToolContext` for state management.\n",
        "*   Orchestrating the flow with the `Runner`.\n",
        "\n",
        "To dive deeper into the capabilities of the Google Agent Development Kit:\n",
        "\n",
        "1.  **Explore the Official Documentation:** For detailed explanations of all components (Agents, Tools, Sessions, Callbacks, Multi-Agent systems, etc.), visit the [**Google ADK Documentation site**](https://google.github.io/adk-docs/).\n",
        "2. **Try the Getting Started Notebook:** Explore the [**official ADK tutorial notebook on Colab**](https://colab.sandbox.google.com/github/google/adk-docs/blob/main/examples/python/tutorial/agent_team/adk_tutorial.ipynb) for a hands-on introduction to building your first agent.\n",
        "3.  **Discover More Examples:** Check out the [**Google ADK GitHub repository**](https://github.com/google/adk-python)  for a wider range of examples, including more complex workflows, integrations, and advanced agent patterns.\n",
        "\n",
        "Consider exploring concepts like:\n",
        "\n",
        "*   **Workflow Agents** (`SequentialAgent`, `ParallelAgent`, `LoopAgent`) for structured process control.\n",
        "*   **Multi-Agent Systems** for building collaborative agent teams.\n",
        "*   Other **Tool Types** (OpenAPI, Google Cloud Tools, Built-in Tools) for broader integrations."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "Getting_started_with_ADK.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
